package chagine.core.lock.redis.util;

import org.springframework.data.redis.core.RedisConnectionUtils;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author zzw
 */
public class SeeingLockRedisUtil {

    private final RedisTemplate<String, LockInfo> redisTemplate;
    private final RedisTemplate<String, Long> longRedisTemplate;

    private static final String EXPIRES_ANNEX = "_expires";

    private static final long KEY_TTL_SEC = 30 * 60;

    SeeingLockRedisUtil(RedisTemplate<String, LockInfo> redisTemplate, RedisTemplate<String, Long> longRedisTemplate) {
        this.redisTemplate = redisTemplate;
        this.longRedisTemplate = longRedisTemplate;
    }

    /**
     * <p>
     * 通过key获取储存在redis中的value
     * </p>
     * <p>
     * 并释放连接
     * </p>
     *
     * @param key
     * @return 成功返回value 失败返回null
     */
    public LockInfo get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * <p>
     * 向redis存入key和value,并释放连接资源
     * </p>
     * <p>
     * 如果key已经存在 则覆盖
     * </p>
     *
     * @param key
     * @param value
     * @return 成功 返回OK 失败返回 0
     */
    public void set(String key, LockInfo value) {
        redisTemplate.opsForValue().set(key, value, KEY_TTL_SEC, TimeUnit.SECONDS);
        longRedisTemplate.opsForValue().set(key + EXPIRES_ANNEX, value.getExpires(), KEY_TTL_SEC, TimeUnit.SECONDS);
    }

    /**
     * <p>
     * 删除指定的key,也可以传入一个包含key的数组
     * </p>
     *
     * @param key 一个key 也可以使 string 数组
     * @return 返回删除成功的个数
     */
    public void del(String key) {
        redisTemplate.delete(key);
        redisTemplate.delete(key + EXPIRES_ANNEX);
    }


    /**
     * <p>
     * 判断key是否存在
     * </p>
     *
     * @param key
     * @return true OR false
     */
    public Boolean exists(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * <p>
     * 设置key value,如果key已经存在则返回0,nx==> not exist
     * </p>
     *
     * @param key
     * @param value
     * @return 成功返回1 如果存在 和 发生异常 返回 0
     */
    public Long setnx(String key, LockInfo value) {
        Boolean absent = redisTemplate.opsForValue().setIfAbsent(key, value);
        if (absent) {
            redisTemplate.expire(key, KEY_TTL_SEC, TimeUnit.SECONDS);
            longRedisTemplate.opsForValue().set(key + EXPIRES_ANNEX, value.getExpires(), KEY_TTL_SEC, TimeUnit.SECONDS);
        }
        return absent ? 1L : 0L;
    }

    /**
     * <p>
     * 设置key的值,并返回一个旧值
     * </p>
     *
     * @param key
     * @param value
     * @return 旧值 如果key不存在 则返回null
     */
    public LockInfo getSet(String key, LockInfo value) {
        LockInfo oldValue = redisTemplate.opsForValue().getAndSet(key, value);
        longRedisTemplate.expire(key, KEY_TTL_SEC, TimeUnit.SECONDS);
        return oldValue;
    }

    public Long getSetAnnex(String key, Long value) {
        Long oldValue = longRedisTemplate.opsForValue().getAndSet(key + EXPIRES_ANNEX, value);
        longRedisTemplate.expire(key + EXPIRES_ANNEX, KEY_TTL_SEC, TimeUnit.SECONDS);
        return oldValue;
    }

    /**
     * 获得 key 的 ttl 时间，单位秒
     *
     * @param key 要获得 ttl 的值
     * @return -1 表示永不超时
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key);
    }

    /**
     * 设置 key 的默认 ttl 时间，单位秒
     *
     * @param key
     * @return
     */
    public Boolean setDefaultExpire(String key) {
        return redisTemplate.expire(key, KEY_TTL_SEC, TimeUnit.SECONDS);
    }

    /**
     * 获得服务端时间
     *
     * @return 服务端时间戳
     */
    public long time() {
        // FIXME
        return System.currentTimeMillis();
    }

    public void watch(String lockKey) {
        redisTemplate.watch(lockKey);
    }

    public void transaction() {
        redisTemplate.setEnableTransactionSupport(true);
        redisTemplate.multi();
    }

    public List<Object> exec() {
        return redisTemplate.exec();
    }

    public void unbindConnection() {
        RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory());
    }
}
